﻿using Sage.DownloadLite.Interfaces;
using Sage.DownloadLite.Utils;
using Sage.DownloadLite.Models;
using Sage.DownloadLite.Exceptions;

namespace Sage.DownloadLite.Core;

/// <summary>
/// HTTP服务实现
/// </summary>
public sealed class HttpService : IHttpService
{
    private static readonly Lazy<HttpService> _instance = new(() => new HttpService());
    private HttpClient? _httpClient;
    private TimeSpan _currentTimeout = DownloadConfiguration.DEFAULT_TIMEOUT;
    private string _currentUserAgent = DownloadConfiguration.DEFAULT_USER_AGENT;
#if NET9_0_OR_GREATER
    private readonly System.Threading.Lock _lockObject = new();
#else
    private readonly object _lockObject = new();
#endif
    private bool _disposed = false;

    /// <summary>
    /// 获取单例实例
    /// </summary>
    public static HttpService Instance => _instance.Value;

    /// <summary>
    /// 私有构造函数
    /// </summary>
    private HttpService()
    {
        // 不在构造函数中创建 HttpClient，延迟到第一次使用时创建
        DebugLogger.WriteMessage("HttpService", "HttpService initialized with lazy HttpClient creation");
    }

    /// <summary>
    /// 设置超时时间
    /// </summary>
    /// <param name="timeout">超时时间</param>
    public void SetTimeout(TimeSpan timeout)
    {
        lock (_lockObject)
        {
            if (_currentTimeout == timeout)
            {
                DebugLogger.WriteMessage("HttpService", $"Timeout already set to {timeout}, skipping");
                return;
            }

            _currentTimeout = timeout;

            // 如果已经创建了 HttpClient，需要重新创建以应用新的超时设置
            if (_httpClient != null)
            {
                DebugLogger.WriteMessage("HttpService", $"Recreating HttpClient due to timeout change: {timeout}");
                _httpClient.Dispose();
                _httpClient = null; // 设置为 null，下次使用时重新创建
            }

            DebugLogger.WriteMessage("HttpService", $"Timeout set to {timeout}");
        }
    }

    /// <summary>
    /// 设置用户代理字符串
    /// </summary>
    /// <param name="userAgent">用户代理字符串</param>
    public void SetUserAgent(string userAgent)
    {
        if (string.IsNullOrWhiteSpace(userAgent))
        {
            DebugLogger.WriteMessage("HttpService", "Invalid user agent provided, ignoring");
            return;
        }
        lock (_lockObject)
        {
            if (_currentUserAgent == userAgent)
            {
                DebugLogger.WriteMessage("HttpService", $"User agent already set to {userAgent}, skipping");
                return;
            }
            _currentUserAgent = userAgent;

            // 如果已经创建了 HttpClient，需要重新创建以应用新的 User-Agent
            if (_httpClient != null)
            {
                DebugLogger.WriteMessage("HttpService", $"Recreating HttpClient due to user agent change: {userAgent}");
                _httpClient.Dispose();
                _httpClient = null;
            }

            DebugLogger.WriteMessage("HttpService", $"User agent set to {userAgent}");
        }
    }

    /// <summary>
    /// 获取或创建 HttpClient 实例
    /// </summary>
    /// <returns>HttpClient 实例</returns>
    private HttpClient GetOrCreateHttpClient()
    {
        if (_httpClient == null)
        {
            lock (_lockObject)
            {
                // 双重检查锁定模式
                if (_httpClient == null)
                {
                    _httpClient = new HttpClient
                    {
                        Timeout = _currentTimeout
                    };
                    _httpClient.DefaultRequestHeaders.Add("User-Agent", _currentUserAgent);
                    DebugLogger.WriteMessage("HttpService", $"Created new HttpClient - Timeout: {_currentTimeout}, User-Agent: {_currentUserAgent}");
                }
            }
        }
        return _httpClient;
    }

    /// <summary>
    /// 异步下载文件
    /// </summary>
    /// <param name="taskInfo">任务信息</param>
    /// <param name="progress">进度回调</param>
    /// <param name="progressInterval">进度更新间隔（百分比）</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>任务结果</returns>
    public async Task<TaskResult> DownloadFileAsync(TaskInfo taskInfo,
        IProgress<SingleDownloadProgress>? progress = null,
        double progressInterval = 1.0,
        CancellationToken cancellationToken = default)
    {
        var result = new TaskResult
        {
            TaskInfo = taskInfo,
            StartTime = DateTime.Now,
            Status = DownloadLite.Enums.TaskStatus.Downloading
        };

        try
        {
            DebugLogger.WriteMessage("HttpService", $"Starting download task {taskInfo.Id}: {taskInfo.Url}");

            taskInfo.Status = DownloadLite.Enums.TaskStatus.Downloading;

            // 确保目录存在
            var directory = Path.GetDirectoryName(taskInfo.FilePath);
            if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
            {
                Directory.CreateDirectory(directory);
                DebugLogger.WriteMessage("HttpService", $"Created directory: {directory}");
            }

            // 使用延迟创建的 HttpClient
            var httpClient = GetOrCreateHttpClient();

            using var response = await httpClient.GetAsync(taskInfo.Url,
                HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);

            response.EnsureSuccessStatusCode();

            var totalBytes = response.Content.Headers.ContentLength ?? -1L;
            DebugLogger.WriteMessage("HttpService", $"Task {taskInfo.Id} total size: {(totalBytes > 0 ? BytesFormatter.Format(totalBytes) : "Unknown")}");

            await using var contentStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
            await using var fileStream = new FileStream(taskInfo.FilePath, FileMode.Create,
                FileAccess.Write, FileShare.None, 8192, true);

            var buffer = new byte[8192];
            long totalBytesRead = 0;
            int bytesRead;

            // 速度和进度计算相关变量
            var startTime = DateTime.Now;
            var lastUpdateTime = startTime;
            var lastBytesRead = 0L;
            var lastReportedPercentage = -1.0;
            const int speedUpdateIntervalMs = 500; // 每500ms更新一次速度

            while ((bytesRead = await contentStream.ReadAsync(buffer.AsMemory(0, buffer.Length), cancellationToken).ConfigureAwait(false)) > 0)
            {
                await fileStream.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false);
                totalBytesRead += bytesRead;

                // 只有在需要报告进度时才计算和报告
                if (progress != null && totalBytes > 0)
                {
                    var currentPercentage = (double)totalBytesRead / totalBytes * 100;

                    // 使用传入的 progressInterval 参数
                    if (currentPercentage - lastReportedPercentage >= progressInterval || currentPercentage >= 100)
                    {
                        var currentTime = DateTime.Now;

                        // 计算下载速度
                        double speed = 0;
                        var timeSinceLastUpdate = (currentTime - lastUpdateTime).TotalMilliseconds;

                        if (timeSinceLastUpdate >= speedUpdateIntervalMs)
                        {
                            var bytesSinceLastUpdate = totalBytesRead - lastBytesRead;
                            var secondsElapsed = timeSinceLastUpdate / 1000.0;
                            speed = bytesSinceLastUpdate / secondsElapsed;

                            lastUpdateTime = currentTime;
                            lastBytesRead = totalBytesRead;
                        }
                        else
                        {
                            // 如果时间间隔太短，使用总体平均速度
                            var totalSeconds = (currentTime - startTime).TotalSeconds;
                            if (totalSeconds > 0)
                            {
                                speed = totalBytesRead / totalSeconds;
                            }
                        }

                        var progressInfo = new SingleDownloadProgress
                        {
                            Task = taskInfo,
                            DownloadedBytes = totalBytesRead,
                            TotalBytes = totalBytes,
                            Percentage = currentPercentage,
                            Speed = speed
                        };

                        progress.Report(progressInfo);
                        lastReportedPercentage = currentPercentage;
                    }
                }
            }

            await fileStream.FlushAsync(cancellationToken).ConfigureAwait(false);

            result.Success = true;
            result.Status = DownloadLite.Enums.TaskStatus.Completed;
            result.FilePath = taskInfo.FilePath;
            result.FileSize = totalBytesRead;
            result.EndTime = DateTime.Now;
            result.Duration = result.EndTime - result.StartTime;

            taskInfo.Status = DownloadLite.Enums.TaskStatus.Completed;
            DebugLogger.WriteMessage("HttpService", $"Task {taskInfo.Id} completed successfully. Size: {BytesFormatter.Format(totalBytesRead)}, Duration: {result.Duration}");
        }
        catch (OperationCanceledException)
        {
            result.Success = false;
            result.Status = DownloadLite.Enums.TaskStatus.Cancelled;
            result.Error = new OperationCanceledException($"Task {taskInfo.Id} was cancelled");
            result.EndTime = DateTime.Now;
            result.Duration = result.EndTime - result.StartTime;

            taskInfo.Status = DownloadLite.Enums.TaskStatus.Cancelled;
            DebugLogger.WriteMessage("HttpService", $"Task {taskInfo.Id} was cancelled");
        }
        catch (Exception ex)
        {
            result.Success = false;
            result.Status = DownloadLite.Enums.TaskStatus.Failed;
            result.Error = new DownloadException($"Download failed for task {taskInfo.Id}: {ex.Message}", taskInfo.Id, ex);
            result.EndTime = DateTime.Now;
            result.Duration = result.EndTime - result.StartTime;

            taskInfo.Status = DownloadLite.Enums.TaskStatus.Failed;
            DebugLogger.WriteMessage("HttpService", $"Task {taskInfo.Id} failed: {ex.Message}");
        }

        return result;
    }

    /// <summary>
    /// 检查文件是否存在
    /// </summary>
    /// <param name="filePath">文件路径</param>
    /// <returns>是否存在</returns>
    public bool FileExists(string filePath)
    {
        return File.Exists(filePath);
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    public void Dispose()
    {
        if (!_disposed)
        {
            lock (_lockObject)
            {
                if (!_disposed)
                {
                    _httpClient?.Dispose();
                    _httpClient = null;
                    _disposed = true;
                    DebugLogger.WriteMessage("HttpService", "HttpService disposed");
                }
            }
        }
    }
}